package com.example.oj_system.common;

import com.example.oj_system.common.complie.CompileRequest;
import com.example.oj_system.common.complie.Compile_Run;
import com.example.oj_system.common.utils.CommandUtils;
import com.example.oj_system.common.utils.FileUtils;
import org.springframework.util.StringUtils;

import java.io.File;

/**
 * 封装用户的每一次编译运行操作
 * 输入: 用户提交的代码
 * 输出: 编译运行的结果
 */
public class Task {
    /**
     *  核心方法: compileAndRun -> 编译运行
     *  参数: 编译运行的 java 源代码
     *  返回值: 编译运行的结果
     */
    public Compile_Run compileAndRun(CompileRequest userCode) {
        Compile_Run answer = new Compile_Run();

        // 0. 创建存放临时文件的目录
        File workDir = new File(AppVariable.WORK_DIR);
        if (!workDir.exists()) {
            // AppVariable.WORK_DIR 有可能是多级目录, 因此使用 mkdirs.
            workDir.mkdirs();
        }

        // 1. 把 userCode 中的 code 写入一个 Solution.java 文件中
        FileUtils.writeFile(AppVariable.COMPILE_CODE, userCode.getCode());

        // 2. 创建子进程, 调用 javac 命令进行编译. -> 得到一个 .class 文件
        //      1) 构造编译命令: 第一个%s表示要编译的文件路径, 第二个表示编译之后的.class文件的保存路径,
        String compileCmd = String.format("javac -encoding utf8 %s -d %s ",
                AppVariable.COMPILE_CODE, AppVariable.WORK_DIR);
        System.out.println("编译命令: " + compileCmd);
        //      2). 对于 javac 命令, 它的标准输出我们不需要关注, 一旦出错, 就会通过标准错误反馈出来
        CommandUtils.run(compileCmd, null, AppVariable.COMPILE_ERROR);
        //      3). 如果编译出错, 错误信息就会被记录到 AppVariable.COMPILE_ERROR 文件中.
        String compileError = FileUtils.readFile(AppVariable.COMPILE_ERROR);
        if (StringUtils.hasLength(compileError)) {
            // 编译出错,错误码为1
            System.out.println("编译出错!");
            return Compile_Run.fail(compileError, 1);
        }

        // 3. 创建子进程, 调用 java 命令并执行.
        //      1). 构造运行命令: -classpath %s -> 指定要运行文件的路径,  %s -> 要运行的 java类名
        String runCmd = String.format("java -classpath %s %s", AppVariable.WORK_DIR, AppVariable.CLASS_NAME);
        System.out.println("运行命令: " + runCmd);
        //      2). 创建子进程, 执行 java命令
        CommandUtils.run(runCmd, AppVariable.RUN_STDOUT, AppVariable.RUN_STDERR);
        //      3) 判断运行是否出错
        String runError = FileUtils.readFile(AppVariable.RUN_STDERR);
        if (StringUtils.hasLength(runError)) {
            // 运行出错,错误码为2
            System.out.println("运行出错");
            return Compile_Run.fail(runError, 2);
        }

        // 4. 父进程获取编译运行的结果, 并打包为 UserAnswer 对象
        return Compile_Run.success(0, FileUtils.readFile(AppVariable.RUN_STDOUT));
    }

    public static void main(String[] args) {
        CompileRequest code = new CompileRequest();
        code.setCode("""
                public class Solution {
                    public static void main(String[] args) {
                        System.out.println("hello world");
                    }
                }""");
        Task task = new Task();
        Compile_Run answer = task.compileAndRun(code);
        System.out.println(answer);
    }
}
